﻿using ListAndLINQ;

namespace LINQ;

class Program
{
    static void Main(string[] args)
    {
        // LINQ 查询语法和方法语法
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        // 查询语法
        var evenNumbers = from n in numbers // 从 numbers 集合中，为每个元素定义一个范围变量 n
            where n % 2 == 0 // 过滤条件：只选择 n 除以 2 余数为 0 的元素
            select n; // 选择符合条件的元素 n 作为结果

        var evenNumbers2 =
            numbers.Where(n => n % 2 == 0); // 在 numbers 集合上调用 Where 扩展方法，Lambda 表达式 n => n % 2 == 0 定义了过滤逻辑
        foreach (int i in evenNumbers2)
        {
            // Console.WriteLine($"Even number: {i}");
        }


        // LINQ 常用操作符

        //过滤 (Filtering): Where(predicate) - 根据指定条件从序列中筛选元素。
        List<Person> list = new List<Person>();
        list.Add(new Person("Alice", 2));
        list.Add(new Person("zhangsan", 4));
        list.Add(new Person("tom", 40));
        list.Add(new Person("tom", 40));
        var where = list.Where(p => p.Age >= 18);
        foreach (Person person in where)
        {
            // Console.WriteLine($"{person.Name} is {person.Age} years old");
        }

        //投影/转换 (Projection): Select(selector) - 将序列中的每个元素转换（映射）成新的类型或形状。
        string[] names = ["Alice", "Tom", "Bob"];

        // 将每个名字转换成它的长度
        IEnumerable<int> select = names.Select(n => names.Length); //3 3 3 

        // 将 Person 对象列表转换成只包含姓名的字符串列表
        IEnumerable<string?> enumerable = list.Select(p => p.Name); //Alice  tomy


        // 排序 (Ordering): OrderBy(keySelector), OrderByDescending(), ThenBy(), ThenByDescending() - 根据一个或多个键对序列进行升序或降序排序。
        var sortedNames = names.OrderBy(name => name); // 按字母顺序排序
        var sortedByAge = list.OrderByDescending(p => p.Age); // 按年龄降序排序
        var sortedByAgeThenName = list.OrderBy(p => p.Age).ThenBy(p => p.Name); // 先按年龄升序，年龄相同再按姓名升序

        // 分组 (Grouping): GroupBy(keySelector) - 根据一个键将序列中的元素分组。
        // 按年龄将人员分组
        var peopleByAge = list.GroupBy(p => p.Age);
        // 结果是 IEnumerable<IGrouping<int, Person>>，每个 IGrouping 包含一个 Key (年龄) 和一组 Person 对象

        //聚合 (Aggregation): Sum(), Average(), Count(), Min(), Max() - 计算序列的某个属性的总和、平均值、数量、最小值、最大值。 Aggregate() - 应用自定义的累加逻辑。
        var totalAge = list.Sum(p => p.Age); // 计算总年龄
        var averageAge = list.Average(p => p.Age); // 计算平均年龄
        var numberOfPeople = list.Count(); // 统计人数
        var oldestAge = list.Max(p => p.Age); // 获取最大年龄

        // Aggregate 是一个更通用的聚合方法，可以实现 Sum, Average 等功能，也可以实现更复杂的逻辑
        // 例如，计算所有名字的总长度：
        var totalNameLength =
            names.Aggregate(0,
                (total, name) => total + name.Length); // 初始值 0, 累加器 (total, name) => total + name.Length  

        // 限定符 (Quantifiers): Any(predicate), All(predicate) - 检查序列中是否存在满足条件的元素 (Any) 或是否所有元素都满足条件 (All)。

        bool hasAdults = list.Any(p => p.Age >= 18); // 是否有至少一个成年人
        bool allAreAdults = list.All(p => p.Age >= 18); // 是否所有人都是成年人
    }


    //349. 两个数组的交集
    public int[] Intersection(int[] nums1, int[] nums2)
    {
        /*IEnumerable<int> enumerable = nums1.Intersect(nums2);
        return enumerable.ToArray();*/
        //或者使用hashSet来
        HashSet<int> set = new HashSet<int>(nums1);
        return nums2.Where(n => set.Contains(n)) // 在 nums2 中过滤出存在于 set1 中的元素
            .Distinct() //去重
            .ToArray(); // 转换为数组
    }

    //1480. 一维数组的动态和
    public int[] RunningSum(int[] nums)
    {
        return nums.Select((x, i) => nums.Take(i + 1).Sum()).ToArray();
    }

    //347 前 K个高频元素(使用LINQ)
    public int[] TopKFrequent(int[] nums, int k)
    {
        if (nums == null || nums.Length == 0 || k <= 0)
        {
            return new int[0];
        }

        return nums.GroupBy(n => n)//先分组   group.Key 是数字，group 是该数字所有出现的集合 group.Count() 是该数字的频率
            .OrderByDescending(group => group.Count()) //根据频次进行降序
            .Take(k)//选取最高的k个分组
            .Select(group => group.Key)//转换为 Select: 从前 K 个分组中，只提取分组的 Key (数字)
            .ToArray();//返回数组
    }
}